package com.tools.scanner.camera

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.net.Uri
import android.os.Handler
import android.os.Looper
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.core.TorchState
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import com.tools.scanner.utils.DataReportUtil
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class CameraXHelper(private val mCameraCallback: OnCameraCallback) {

    private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
    private val mHandler = Handler(Looper.getMainLooper())
    private var mCameraModel = CameraModel.QR_CODE
    private var mCameraController: LifecycleCameraController? = null
    private var mParentFile: File? = null
    private var mCameraAnalyzer: CameraXAnalyzer? = null
    private var mFlashIsOpen = false

    fun setCameraModel(model: CameraModel) {
        this.mCameraModel = model
        setAnalyzerEnable()
    }

    fun getCameraModel(): CameraModel {
        return mCameraModel
    }

    private fun setAnalyzerEnable() {
        mCameraAnalyzer?.setEnable(mCameraModel == CameraModel.QR_CODE)
    }

    fun startCamera(
        context: Context,
        lifecycleOwner: LifecycleOwner,
        previewView: PreviewView
    ) {
        mParentFile = context.cacheDir
        mCameraController = LifecycleCameraController(context).apply {
            mCameraAnalyzer = CameraXAnalyzer(this, mCameraCallback)
            setAnalyzerEnable()
            setImageAnalysisAnalyzer(
                ContextCompat.getMainExecutor(context),
                mCameraAnalyzer!!
            )

            torchState.observe(lifecycleOwner) { state ->
                when (state) {
                    TorchState.ON -> {
                        mFlashIsOpen = true
                    }

                    TorchState.OFF -> {
                        mFlashIsOpen = false
                    }
                }
            }

            bindToLifecycle(lifecycleOwner)
            previewView.controller = this
        }
    }

    fun takePicture() {
        val parentFile = mParentFile ?: return

        val file = File(parentFile, "${System.currentTimeMillis()}.png")

        mCameraController?.takePicture(
            cameraExecutor,
            object : ImageCapture.OnImageCapturedCallback() {
                override fun onCaptureStarted() {
                    mCameraCallback.onCaptureStart()
                }

                override fun onError(exception: ImageCaptureException) {
                    exception.printStackTrace()
                    mHandler.post {
                        DataReportUtil.reportCaptureFail(exception.toString())
                        mCameraCallback.onCaptureFinish(null)
                    }
                }

                override fun onCaptureSuccess(image: ImageProxy) {
                    val uri: Uri? = try {
                        val rotation = image.imageInfo.rotationDegrees
                        val buffer = image.planes[0].buffer
                        val bytes = ByteArray(buffer.remaining())
                        buffer.get(bytes)
                        image.close()

                        val rotatedBitmap = rotateJpegBytes(bytes, rotation)
                        FileOutputStream(file).use { fos ->
                            rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos)
                        }
                        rotatedBitmap.recycle()

                        Uri.fromFile(file)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        null
                    }

                    mHandler.post {
                        mCameraCallback.onCaptureFinish(uri)
                    }
                }

            })
    }

    private fun rotateJpegBytes(jpegData: ByteArray, rotation: Int): Bitmap {
        val matrix = Matrix().apply { postRotate(rotation.toFloat()) }
        val bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.size)
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true).also {
            bitmap.recycle()
        }
    }

    fun switchFlash(): Boolean {
        mFlashIsOpen = !mFlashIsOpen
        mCameraController?.enableTorch(mFlashIsOpen)
        return mFlashIsOpen
    }

    fun release() {
        mFlashIsOpen = false
        mCameraAnalyzer?.setEnable(false)
        mCameraAnalyzer = null
        mCameraController?.unbind()
        mCameraController = null
    }

    enum class CameraModel {
        QR_CODE,
        OCR
    }

}